package mirrormonkey.framework.entity;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;

import mirrormonkey.core.annotations.NotifyLifecycleListeners;
import mirrormonkey.framework.EntityProvider;
import mirrormonkey.framework.SyncAppState;
import mirrormonkey.framework.member.MemberDataIR;
import mirrormonkey.framework.member.StaticMemberData;
import mirrormonkey.util.annotations.hfilter.ClassFilter;
import mirrormonkey.util.annotations.parsing.ClassIR;
import mirrormonkey.util.annotations.parsing.MemberIR;

public class StaticEntityDataIR extends ClassIR {

	protected final Collection<Class<?>> parsedClasses;

	protected Class<?> localClass;

	protected Class<?> connectedClass;

	public StaticEntityDataIR(ClassIR previous, Class<?> forClass) {
		super(previous, forClass);
		parsedClasses = new LinkedList<Class<?>>();
		addCollectType(NotifyLifecycleListeners.class);
	}

	@Override
	public void parseInterfaceClass(Class<?> c) {
		super.parseInterfaceClass(c);
		parsedClasses.add(c);
	}

	@Override
	public void parseHierarchyClass(Class<?> c) {
		super.parseHierarchyClass(c);
		parsedClasses.add(c);
	}

	public List<MemberDataIR> getSortedMemberList() {
		List<MemberDataIR> l = new ArrayList<MemberDataIR>();
		for (Class<?> c : new Class<?>[] { Field.class, Method.class }) {
			for (MemberIR i : memberIRs.get(c)) {
				l.add((MemberDataIR) i);
			}
		}
		Collections.sort(l);
		return l;
	}

	public List<MemberDataIR> getSortedConstrList() {
		List<MemberDataIR> l = new ArrayList<MemberDataIR>();
		for (MemberIR i : memberIRs.get(Constructor.class)) {
			l.add((MemberDataIR) i);
		}
		Collections.sort(l);
		return l;
	}

	public StaticEntityData extractData(Class<? extends SyncEntity> localClass,
			Class<? extends SyncEntity> connectedClass, SyncAppState appState,
			boolean collectLocalConstructors) {
		this.localClass = localClass;
		this.connectedClass = connectedClass;

		StaticEntityDataIR connectedIR = (StaticEntityDataIR) parser
				.parseClass(connectedClass, EntityProvider.INTERFACE_BOUND);

		List<MemberDataIR> constructors = collectLocalConstructors ? getSortedConstrList()
				: connectedIR.getSortedConstrList();
		List<MemberDataIR> localMembers = getSortedMemberList();
		List<MemberDataIR> connectedMembers = connectedIR.getSortedMemberList();

		List<StaticMemberData> extractedMembers = new ArrayList<StaticMemberData>();
		for (MemberDataIR ir : constructors) {
			extractedMembers.add(ir.extractData(extractedMembers.size(), this,
					appState, null));
		}

		int searchFrom = 0;
		for (MemberDataIR ir : localMembers) {
			MemberDataIR connectedMemberIR = null;
			SearchConnected: for (int cir = searchFrom; cir < connectedMembers
					.size(); cir++) {
				if (ir.equals(connectedMembers.get(cir))) {
					connectedMemberIR = connectedMembers.get(cir);
					searchFrom = cir + 1;
					break SearchConnected;
				}
			}
			StaticMemberData smd = ir.extractData(extractedMembers.size(),
					this, appState, connectedMemberIR);
			if (smd != null) {
				extractedMembers.add(smd);
			}
		}

		NotifyLifecycleListeners clc = getCollectedAnnotation(NotifyLifecycleListeners.class);
		Collection<Class<?>> notifyClasses = new LinkedList<Class<?>>();
		notifyClasses.addAll(parsedClasses);
		ClassFilter.Eval.filter(clc.value(), notifyClasses);

		return new StaticEntityData(localClass, connectedClass,
				extractedMembers.toArray(new StaticMemberData[extractedMembers
						.size()]),
				notifyClasses.toArray(new Class<?>[notifyClasses.size()]));
	}

	public Collection<Class<?>> extractNotifyClasses() {
		Collection<Class<?>> result = new LinkedHashSet<Class<?>>();
		result.addAll(parsedClasses);
		return result;
	}
}
